package com.huawei.rbac.test;

import org.junit.Test;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * CompletableFuture实现了Future接口，并在此基础上进行了丰富的扩展，完美弥补了Future的局限性，同时CompletableFuture实现了对任务编排的能力
 * 借助这项能力，可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说，这项能力是它的核心能力。
 *
 * @author 罗华
 * @date 2022/4/15
 */
public class CompletableFutureDemo {


    public static void main(String[] args) throws ExecutionException, InterruptedException {

        Runnable runnable = () -> System.out.println("无返回结果异步任务");
        CompletableFuture.runAsync(runnable);

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("有返回值的异步任务");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello World";
        });
        // 获取任务的返回值
        String result = future.get();
        System.out.println(result);

        /**
         * thenApply 和 thenCompose的区别：
         *
         * thenApply转换的是泛型中的类型，返回的是同一个CompletableFuture；
         * thenCompose将内部的CompletableFuture调用展开来并使用上一个CompletableFuture调用的结果
         * 在下一步的CompletableFuture调用中进行运算，是生成一个新的CompletableFuture。
         */
        //  thenApply接收一个函数作为参数，使用该函数处理上一个CompletableFuture调用的结果，并返回一个具有处理结果的Future对象
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            int result1 = 100;
            System.out.println("第一次运算：" + result1);
            return result1;
        }).thenApply(number -> {
            int result2 = number * 3;
            System.out.println("第二次运算：" + result2);
            return result2;
        });
        System.out.println(future2.get());

        // thenCompose的参数为一个返回CompletableFuture实例的函数，该函数的参数是先前计算步骤的结果。
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(30);
            System.out.println("第一次运算：" + number);
            return number;
        }).thenCompose(param -> CompletableFuture.supplyAsync(() -> {
            int number = param * 2;
            System.out.println("第二次运算：" + number);
            return number;
        }));

        System.out.println(future1.get());


        // thenAccept()对单个结果消费
        CompletableFuture<Void> futures = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("第一次运算：" + number);
            return number;
        }).thenAccept(number -> System.out.println("第二次运算：" + number * 5));

        // thenAcceptBoth()对两个结果进行消费
        CompletableFuture<Integer> future5 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(3) + 1;
            System.out.println("任务1结果：" + number);
            return number;
        });

        CompletableFuture<Integer> future6 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(3) + 1;
            System.out.println("任务2结果：" + number);
            return number;
        });

        future5.thenAcceptBoth(future6, (x, y) -> System.out.println("最终结果：" + (x + y)));

        // thenRun也是对线程任务结果的一种消费函数，与thenAccept不同的是，thenRun会在上一阶段
        // CompletableFuture计算完成的时候执行一个Runnable，而Runnable并不使用该CompletableFuture计算的结果。
        CompletableFuture<Void> future3 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("第一阶段：" + number);
            return number;
        }).thenRun(() -> System.out.println("thenRun 执行"));


    }

    /**
     * thenCombine
     * 合并两个线程任务的结果，并进一步处理
     *
     * @throws Exception
     */
    @Test
    public void testThenCombine() throws Exception {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("任务1结果：" + number);
            return number;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("任务2结果：" + number);
            return number;
        });
        CompletableFuture<Integer> result = future1.thenCombine(future2, Integer::sum);
        System.out.println("组合后结果：" + result.get());

    }

    /**
     * applyToEither
     * 两个线程任务相比较，先获得执行结果的，就对该结果进行下一步的转化操作。
     *
     * @throws Exception
     */
    @Test
    public void testApplyToEither() throws Exception {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("任务1结果:" + number);
            return number;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("任务2结果:" + number);
            return number;
        });

        future1.applyToEither(future2, number -> {
            System.out.println("最快结果：" + number);
            return number * 2;
        });
    }

    /**
     * anyOf()的参数是多个给定的 CompletableFuture，当其中的任何一个完成时，方法返回这个 CompletableFuture
     *
     * @throws Exception
     */
    @Test
    public void testAnyOf() throws Exception {

        Random random = new Random();
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(random.nextInt(5));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(random.nextInt(1));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "world";
        });
        CompletableFuture<Object> result = CompletableFuture.anyOf(future1, future2);
        System.out.println("result = " + result.get());
    }

    /**
     * allOf方法用来实现多 CompletableFuture 的同时返回。
     *
     * @throws Exception
     */
    @Test
    public void testAllOf() throws Exception {

        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("future1完成！");
            return "future1完成！";
        });

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("future2完成！");
            return "future2完成！";
        });

        CompletableFuture<Void> completableFuture = CompletableFuture.allOf(future1, future2);

        try {
            completableFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

    }

}
